#pragma once 
#include <future>
#include "../common/net.hpp"
#include "../common/message.hpp"

namespace yjz_rpc
{
    namespace client
    {
        class Requestor
        {
        public:
            using ptr = std::shared_ptr<Requestor>;
            using AsyncResponse = std::future<BaseMessage::ptr>;
            using RequestCallBack = std::function<void(const BaseMessage::ptr&)>;
            struct RequestDescribe
            {
                using ptr = std::shared_ptr<RequestDescribe>;
                RType rtype;
                RequestCallBack callback;
                BaseMessage::ptr request;
                std::promise<BaseMessage::ptr> response;
            };

            void onResponse(const BaseConnection::ptr& conn, BaseMessage::ptr& msg)
            {
                std::string rid = msg->getId();
                RequestDescribe::ptr rd = getDescribe(rid);
                if (rd.get() == nullptr)
                {
                    LOG(LogLevel::ERROR) << "未找到对应的请求描述！";
                    std::cout << rd << std::endl;
                    return;
                }
                if (rd->rtype == RType::REQ_ASYNC)
                {
                    rd->response.set_value(msg);
                }
                else if (rd->rtype == RType::REQ_CALLBACK)
                {
                    if (rd->callback)
                    {
                        rd->callback(msg);
                    }
                }
                else
                {
                    LOG(LogLevel::ERROR) << "未知请求类型！";
                }
                delDescribe(rid);
            }

            bool send(const BaseConnection::ptr& conn, const BaseMessage::ptr& req, AsyncResponse& async_rsp)
            {
                RequestDescribe::ptr rd = newDescribe(req, RType::REQ_ASYNC);
                if (rd.get() == nullptr)
                {
                    LOG(LogLevel::ERROR) << "构造请求描述对象失败！";
                    return false;
                }
                conn->send(req);
                async_rsp = rd->response.get_future();
                return true;
            }

            bool send(const BaseConnection::ptr& conn, const BaseMessage::ptr& req, BaseMessage::ptr& rsp)
            {
                AsyncResponse rsp_future;
                bool res = send(conn, req, rsp_future);
                if (res == false)
                {
                    return false;
                }
                rsp = rsp_future.get();
                return true;
            }

            bool send(const BaseConnection::ptr& conn, const BaseMessage::ptr& req, RequestCallBack& cb)
            {
                RequestDescribe::ptr rd = newDescribe(req, RType::REQ_CALLBACK, cb);
                if (rd.get() == nullptr)
                {
                    LOG(LogLevel::ERROR) << "构造请求描述对象失败！";
                    return false;
                }
                conn->send(req);
                return true;
            }
        private:
            RequestDescribe::ptr newDescribe(const BaseMessage::ptr& req, RType rtype
                , const RequestCallBack& cb = RequestCallBack())
            {
                std::unique_lock<std::mutex> lock(_mutex);
                RequestDescribe::ptr rd = std::make_shared<RequestDescribe>();
                rd->request = req;
                rd->rtype = rtype;
                if (rtype == RType::REQ_CALLBACK && cb)
                {
                    rd->callback = cb;
                }
                _request_desc.insert(std::make_pair(req->getId(), rd));
                return rd;
            }

            RequestDescribe::ptr getDescribe(const std::string& rid)
            {
                auto it = _request_desc.find(rid);
                if (it == _request_desc.end())
                {
                    return RequestDescribe::ptr();
                }
                return it->second;
            }

            void delDescribe(const std::string& rid)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _request_desc.find(rid);
                if (it != _request_desc.end())
                {
                    _request_desc.erase(rid);
                }
            }
        private:
            std::mutex _mutex;
            std::unordered_map<std::string, RequestDescribe::ptr> _request_desc;
        };
    }
}